package ocp.context;

import ocp.context.adapter.*;

/**
 * Situational contexts are supposed to change often, so a mechanism for
 * updating its situation is needed. These contexts use a pull mechanism for
 * retrieving the data and sending it to its observers. Every situational 
 * context has an internal thread that can be activated at creation time and
 * wakes every certain time, updating the contextual information if needed.
 * @author Ignacio Nieto Carvajal
 */
public class SituationalContext extends SimpleContext implements java.io.Serializable {

    /** time between contextual information updates (default 15 secs) */
    protected long updateTime = 15000;
    /** update thread */
    protected ContextUpdateThread thread;
    /** show error messages when contextual information is not accesible? */
    protected boolean debug = false;

    /** This thread awakes every updateTime milliseconds and invokes the
     * updateContextualInformation method, thus updating the contextual
     * information from this context.
     * @author Ignacio Nieto Carvajal.
     */
    class ContextUpdateThread extends Thread implements java.io.Serializable {

        boolean threadSuspended = false;

        public void run() {
            try {
                while (true) {
                    try {
                        updateContextualInformation();
                    } catch (ContextualInformationAbsentException ciae) {
                        if (debug) {
                            System.out.println(
                                    "Error obtaining contextual information from" +
                                    getName());
                        }
                    }

                    Thread.currentThread().sleep(updateTime);

                    if (threadSuspended) {
                        synchronized (this) {
                            while (threadSuspended) {
                                wait();
                            }
                        }
                    }

                }
            } catch (InterruptedException ie) {
            }
        }
    }

    /** Main constructor of the class. Sets default values and starts the
     * thread that updates periodically the contextual information */
    public SituationalContext(String name, String id) {
        super(name, id);
        thread = new ContextUpdateThread();
        thread.start();
    }

    /** updates the contextual information and notifies the observers */
    public void updateContextualInformation() throws ContextualInformationAbsentException {
        super.updateContextualInformation();
        contextChanged(getContextualInformation());
    }

    /** Alternate constructor of the class. It allow to choose wheter or not
     * start the context update thread.
     * @param time update time in milliseconds. Only values greater than zero are considered.
     * @param startUpdateThread if true, the update thread will be launched.
     */
    public SituationalContext(String name, String id, long time,
            boolean startUpdateThread) {
        super(name, id);
        if (time > 0) {
            updateTime = time;
        }
        if (startUpdateThread) {
            thread = new ContextUpdateThread();
            thread.start();
        }
    }

    /** And yet another constructor of the class. It allow to choose wheter or 
     * not start the context update thread and also sets the debug variable
     * that indicates wheter to not to debug error information about context.
     * @param time update time in milliseconds. Only values greater than zero are considered.
     * @param startUpdateThread if true, the update thread will be launched.
     * @param debug if true, error messages will be generated on contextual information retrieving fails.
     */
    public SituationalContext(String name, String id, long time,
            boolean startUpdateThread, boolean debug) {
        super(name, id);
        if (time > 0) {
            updateTime = time;
        }
        this.debug = debug;
        if (startUpdateThread) {
            thread = new ContextUpdateThread();
            thread.start();
        }
    }

    /**
     * Suspends the running ContextUpdateThread, thus stopping the contextual
     * information pulling mechanism.
     */
    public void suspendContextualUpdate() {
        thread.threadSuspended = true;
    }

    /**
     * Restarts the running ContextUpdateThread, thus resuming the contextual
     * information pulling mechanism.
     */
    public void resumeContextualUpdate() {
        thread.threadSuspended = false;
    }

    /**
     * Sets the update time of the ContextUpdateThread. Only values greater than
     * zero are considered.
     * @param time new update time in milliseconds
     */
    public void setUpdateTime(long time) {
        if (time > 0) {
            updateTime = time;
        }
    }

    /** Sets the debug variable. If set to true, error messages will be
     * loged to stderr. */
    public void setDebug(boolean debug) {
        this.debug = debug;
    }
}
